package com.tengol.course.spring.annotation.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;

/**
 * LogAspect
 *
 * @author dongrui
 * @date 2020/4/26 10:35
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    /**
     * 抽取公共的切入点表达式
     * （1）本类引用
     * （2）其它的切面引用
     */
    @Pointcut("execution(public int com.tengol.course.spring.annotation.aop.MathCalculator.*(..))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void logStart(JoinPoint point) {
        String methodName = point.getSignature().getName();
        Object[] args = point.getArgs();
        log.info("=== @Before 调用方法：{}，入参是：{}", methodName, args);
    }

    @After("pointCut()")
    public void logEnd(JoinPoint point) {
        String methodName = point.getSignature().getName();
        log.info("=== @After 方法结束：{}", methodName);
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint point, Object result) {
        String methodName = point.getSignature().getName();
        log.info("=== @AfterReturning 方法正常结束：{}，结果：{}", methodName, result);
    }

    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void logThrow(JoinPoint point, Exception ex) {
        String methodName = point.getSignature().getName();
        log.info("=== @AfterThrowing 方法执行异常：{}，异常信息：{}", methodName, ex.getMessage(), ex);
    }

    @Around("pointCut()")
    public Object round(ProceedingJoinPoint point) {
        String methodName = point.getSignature().getName();
        Instant start = Instant.now();
        try {
            Object result = point.proceed();
            log.info("=== @Around 执行正常结束，结果：{}",result);
            return result;
        } catch (Throwable throwable) {
            log.info("=== @Around 方法执行异常：{}，异常信息：{}", methodName, throwable.getMessage(), throwable);
            return null;
        } finally {
            Instant end = Instant.now();
            long times = Duration.between(start, end).toMillis();
            log.info("=== @Around 方法 {} 总耗时：{}", methodName, times);
        }
    }
}
